
//
// 120929 - AH - Created.
// 121117 - AH - addAll(Collection<E>, E[]).
//

package org.japril.jutil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;

/**
 * <p>
 *   Utility methods of use when working with Java collection including native
 *   arrays. 
 *   
 *   Complements 
 *   {@link Arrays} and
 *   {@link Collections}.
 * </p>
 * @author Arne Halvorsen (AH)
 */
public final class JCollections 
{
  private JCollections(){} // Utility pattern dictates private constructor.
  
  /**
   * <p>
   *   Search for objects of a given type in a list.  
   * </p>
   * @param <T>   Type searched. 
   * @param l     List of object to search.
   * @param clazz Type of objects to search.
   * @return Found.
   */
  @SuppressWarnings("unchecked")
  public static <T> List<T> findTypes(List<?> l, Class<? extends T> clazz)
  {
    if (l==null)
    {
      throw new NullPointerException("l");
    }
    if (clazz==null)
    {
      throw new NullPointerException("clazz");
    }
    
    List<T> retVal=new ArrayList<T>();
    
    for (Object curr : l) 
    {
      if (curr!=null)
      {
        if (clazz.isAssignableFrom(curr.getClass()))
        {
          retVal.add((T)curr);
        }
      }
    }
    
    return retVal;
  }
  
  /**
   * <p>
   *   Search for objects of a given type in an array.  
   * </p>
   * @param <T>   Type searched. 
   * @param a     Array of object to search.
   * @param clazz Type of objects to search.
   * @return Found.
   */
  @SuppressWarnings("unchecked")
  public static <T> List<T> findTypes(Object[] a, Class<? extends T> clazz)
  {
    if (a==null)
    {
      throw new NullPointerException("a");
    }
    if (clazz==null)
    {
      throw new NullPointerException("clazz");
    }
    
    List<T> retVal=new ArrayList<T>();
    
    for (Object curr : a) 
    {
      if (curr!=null)
      {
        if (clazz.isAssignableFrom(curr.getClass()))
        {
          retVal.add((T)curr);
        }
      }
    }
    
    return retVal;
  }
  
  /**
   * <p>
   *   Selects a random element in an array.
   * </p>
   * @param <T> Content type.
   * @param a   Array.
   * @return Random element or {@code null} if {@code a} is empty.
   */
  public static <T> T getRandom(T[] a)
  {
    if (a==null)
    {
      throw new NullPointerException("a");
    }
    
    int n=a.length;
    switch (n)
    {
      case 0 : return null;
      case 1 : return a[0];
      default :
      {
        Random rnd=new Random();
        return a[rnd.nextInt(n)];
      }
    }
  }
  
  /**
   * <p>
   *   Selects a random element in a list.
   * </p>
   * @param <T> Content type.
   * @param l   List.
   * @return Random element or {@code null} if {@code l} is empty.
   */
  public static <T> T getRandom(List<T> l)
  {
    if (l==null)
    {
      throw new NullPointerException("l");
    }
    
    int n=l.size();
    switch (n)
    {
      case 0 : return null;
      case 1 : return l.get(0);
      default :
      {
        Random rnd=new Random();
        return l.get(rnd.nextInt(n));
      }
    }
  }
  
  /**
   * <p>
   *   Gets the index of an element in an array.
   * </p>
   * @param <T> Content type.
   * @param a   Array.
   * @param e   Element, may be {@code null}.
   * @return Index or {@code -1} if not found.
   */
  public static <T> int getIndexOf(T[] a, T e)
  {
    if (a==null)
    {
      throw new NullPointerException("a");
    }
    
    int n=a.length;
    for (int i=0; i<n; i++) if (a[i]==e) return i;
    return -1;
  }
 
  /**
   * <p>
   *   Produces the reverse list of a given list.
   * </p>
   * @param <E>  Content type.
   * @param list List to reverse.
   * @return Reversed list.
   */
  public static <E> List<E> reverse(List<E> list)
  {
    if (list==null)
    {
      throw new NullPointerException("list");
    }
    
    List<E> retVal=new ArrayList<E>();
    
    ListIterator<E> i=list.listIterator(list.size());
    while (i.hasPrevious())
    {
      retVal.add(i.previous());
    }
    
    return retVal;
  }
  
  /**
   * <p>
   *   Adds all elements of an array to a 
   *   {@link Collection}.
   * </p>
   * @param <T>  Type of 
   * @param coll {@link Collection} to add to.
   * @param a    Array with elements to add.
   * @return {@code coll}.
   */
  public static <E> Collection<E> addAll(Collection<E> coll, E[] a)
  {
    if (coll==null)
    {
      throw new NullPointerException("coll");
    }
    if (a==null)
    {
      throw new NullPointerException("a");
    }
    
    for (E e : a) coll.add(e);
    
    return coll;
  }
    
}
